package com.jdy.haoduoaiteacher.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;

import com.jdy.haoduoaiteacher.util.Tools;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zp 2016-4-28 下午5:34:43
 */
public class StorageIndicator extends View {

  protected int WIDTH;

  protected int HEIGHT;

  private final Paint mPaint;

  private final int indicatorColor = Color.argb(255, 249, 176, 48);

  private final int indicatorBorderColor = Color.argb(255, 255, 255, 255);

  protected Path semiCirclePath;

  protected Path pointerPath;

  public PointF Center;

  public float OutterRadius = 15;

  public float InnerRadius = 6;

  public float PointerRadius = 24;

  private float strokeWidth = 6;

  public static final int SAMPLE_COUNT = 20;

  private final Runnable updateRotationRunnable;

  private float pointerRotation = 270;

  private final Handler mHandler = new Handler();

  private boolean forward = true;

  private float ratio = 1;

  /**
   * @param context
   */
  public StorageIndicator(Context context) {
    this(context, null);
    // TODO Auto-generated constructor stub
  }

  /**
   * @param context
   * @param attrs
   */
  public StorageIndicator(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    // TODO Auto-generated constructor stub
  }

  /**
   * @param context
   * @param attrs
   * @param defStyleAttr
   */
  public StorageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    // TODO Auto-generated constructor stub

    OutterRadius *= ratio;

    InnerRadius *= ratio;

    PointerRadius *= ratio;

    strokeWidth *= ratio;

    ratio = Tools.getPixelRatio(context);

    mPaint = new Paint();

    mPaint.setAntiAlias(true);

    mPaint.setStyle(Style.STROKE);

    mPaint.setStrokeWidth(strokeWidth);

    mPaint.setColor(indicatorColor);

    semiCirclePath = new Path();

    pointerPath = new Path();

    updateRotationRunnable = new Runnable() {
      @Override
      public void run() {
        try {
          if (forward) {
            pointerRotation++;
            if (pointerRotation > 90) {
              forward = false;
            }
          } else {
            pointerRotation--;
            if (pointerRotation < -90) {
              forward = true;
            }
          }

          invalidate();
        } finally {
          // 100% guarantee that this always happens, even if
          // your update method throws an exception
          mHandler.postDelayed(updateRotationRunnable, 30);
        }
      }
    };

    // updateRotationRunnable.run();
  }

  /**
   * Convert current storage usage to pointerRotation,in bytes
   * 
   * @param current
   *          should be same unit with max
   * @param max
   *          should be same unit with current
   */
  public void convert2Angle(final float current, final float max) {
    if (max == 0)
      return;
    mHandler.postDelayed(new Runnable() {

      @Override
      public void run() {
        // TODO Auto-generated method stub
        pointerRotation = 180 * current / max - 90;
        invalidate();
      }
    }, 100);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldW, int oldH) {
    WIDTH = w;

    HEIGHT = h;

    Center = new PointF(WIDTH / 2, HEIGHT - HEIGHT / 6);

    OutterRadius = WIDTH / 3;

    OutterRadius *= ratio;

    InnerRadius *= ratio;

    PointerRadius = OutterRadius + InnerRadius;

    getSemiCirclePath();

    getPointerPath();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = 60;

    int desiredHeight = 35;

    desiredWidth *= ratio;

    desiredHeight *= ratio;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;

    int height;

    // Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
      // Must be this size
      width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      width = Math.min(desiredWidth, widthSize);
    } else {
      // Be whatever you want
      width = desiredWidth;
    }

    // Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
      // Must be this size
      height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      height = Math.min(desiredHeight, heightSize);
    } else {
      // Be whatever you want
      height = desiredHeight;
    }

    // MUST CALL THIS
    setMeasuredDimension(width, height);
  }

  private void getPointerPath() {

    pointerPath.reset();

    PointF[] points = getPointsByAngleRange(Center, InnerRadius, 0, Math.PI,
        SAMPLE_COUNT);

    PointF apex = getPointByAngle(Center, PointerRadius, Math.PI * 3 / 2);

    int i = 0;

    int size = points.length;

    pointerPath.moveTo(apex.x, apex.y);

    for (; i < size; i++) {
      float x = points[i].x;
      float y = points[i].y;
      pointerPath.lineTo(x, y);
    }

    pointerPath.lineTo(apex.x, apex.y);

  }

  private void getSemiCirclePath() {
    PointF[] points = getPointsByAngleRange(Center, OutterRadius, Math.PI,
        Math.PI * 2, SAMPLE_COUNT);
    semiCirclePath.reset();
    int i = 0;
    int size = points.length;
    for (; i < size; i++) {
      float x = points[i].x;
      float y = points[i].y;
      if (i == 0) {
        semiCirclePath.moveTo(x, y);
      } else {
        semiCirclePath.lineTo(x, y);
      }
    }
  }

  private PointF[] getPointsByAngleRange(PointF center, float radius,
      double start, double end, int samples) {
    double range = end - start;
    double step = range / samples;
    List<PointF> points = new ArrayList<PointF>();
    double c = start;
    for (; c <= end; c += step) {
      PointF n = getPointByAngle(center, radius, c);
      points.add(n);
    }
    return points.toArray(new PointF[points.size()]);
  }

  /**
   * Get point from the circle by a certain angle.
   */
  private PointF getPointByAngle(PointF center, float radius, double rotateAngle) {
    PointF p = new PointF();
    p.x = center.x + radius * (float) Math.cos(rotateAngle);
    p.y = center.y + radius * (float) Math.sin(rotateAngle);
    return p;
  }

  @Override
  protected void onDraw(Canvas canvas) {

    mPaint.setColor(indicatorColor);

    mPaint.setStyle(Style.STROKE);

    canvas.drawPath(semiCirclePath, mPaint);

    canvas.save();

    canvas.rotate(pointerRotation, Center.x, Center.y);

    mPaint.setStyle(Style.FILL);

    mPaint.setColor(indicatorBorderColor);

    canvas.scale(1.6f, 1.6f, Center.x, Center.y);

    canvas.drawPath(pointerPath, mPaint);

    canvas.restore();

    canvas.save();

    mPaint.setStyle(Style.FILL);

    canvas.rotate(pointerRotation, Center.x, Center.y);

    canvas.scale(1f, 1f, Center.x, Center.y);

    mPaint.setColor(indicatorColor);

    canvas.drawPath(pointerPath, mPaint);

    canvas.restore();

  }

}
